%%%-------------------------------------------------------------------
%% @doc chat_channel_manage public API
%% @end
%%%-------------------------------------------------------------------

-module(chat_channel_manage).
-behaviour(gen_server).

%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------

%%-include("debug.hrl").
-define(DEBUG(Format, Args),  io:format("~n-- " ++ Format ++ "~n", Args)).
-define(DEBUG(Format),  io:format("~n-- " ++ Format ++ "~n")).


%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([ start_link/1
        , init/1
        , handle_call/3
        , handle_cast/2
        , handle_info/2
        , terminate/2
        , code_change/3
        ]).

-export([ login/2
        , logout/2
        , talk/2
        , fetch/2
        ]).

%% ====================================================================
-record(state, { config
               , channel_dict
               }).

%% ====================================================================
%% API
%% ====================================================================
login(Channel, Subscriber) ->
    gen_server:call(?MODULE, {subscribe, Channel, Subscriber}).

logout(Channel, Subscriber) ->
    gen_server:call(?MODULE, {unsubscribe, Channel, Subscriber}).

talk(Channel, Message) ->
    gen_server:call(?MODULE, {talk, Channel, Message}).

fetch(Channel, MsgId) ->
    gen_server:call(?MODULE, {fetch, Channel, MsgId}).

%% ====================================================================
%% internal functions
%% ====================================================================
find_or_create_channel(Channel, #state{channel_dict = ChannelDict, config = Config} = State) ->
    case dict:find(Channel, ChannelDict) of
        {ok, Pid} ->
            {Pid, State};
        _ ->
            {ok, Pid} = chat_channel_sup:start_channel(Config, Channel),
            ChannelDict1 = dict:store(Channel, Pid, ChannelDict),
            {Pid, State#state{ channel_dict = ChannelDict1 }}
    end.


%% ====================================================================
%% Behavioural functions
%% ====================================================================
do_init(Config) ->
    {ok, #state{config = Config, channel_dict = dict:new()}}.

%%--------------------------------------------------------------------
do_call({subscribe, Channel, Subscriber}, From, State) ->
    {ChannelPid, State1} = find_or_create_channel(Channel, State),
    gen_server:cast(ChannelPid, {From, subscribe, Subscriber}),
    {noreply, State1};

do_call({unsubscribe, Channel, Subscriber}, From, State) ->
    {ChannelPid, State1} = find_or_create_channel(Channel, State),
    gen_server:cast(ChannelPid, {From, unsubscribe, Subscriber}),
    {noreply, State1};

do_call({talk, Channel, Message}, From, State) ->
    {ChannelPid, State1} = find_or_create_channel(Channel, State),
    gen_server:cast(ChannelPid, {From, talk, Message}),
    {noreply, State1};

do_call({fetch, Channel, MsgId}, From, State) ->
    {ChannelPid, State1} = find_or_create_channel(Channel, State),
    gen_server:cast(ChannelPid, {From, fetch, MsgId}),
    {noreply, State1};

do_call(Request, _From, State) ->
    ?DEBUG("chat_channel_manage call:~p", [Request]),
    {reply, ok, State}.

%%--------------------------------------------------------------------

do_cast(Request, State) ->
    ?DEBUG("chat_channel_manage cast:~p", [Request]),
    {noreply, State}.

%%--------------------------------------------------------------------


do_info(Info, State) ->
    ?DEBUG("chat_channel_manage info:~p", [Info]),
    {noreply, State}.

%%--------------------------------------------------------------------
do_terminate(_Reason, _State) ->
    ?DEBUG("do_terminate:~p ~p", [?MODULE, _Reason]),
    ok.

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link(Config :: term()) ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link(Config) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, Config, []).

%% ====================================================================
%% Behavioural functions
%% ====================================================================

%% init/1
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:init-1">gen_server:init/1</a>
-spec init(Args :: term()) -> Result when
    Result :: {ok, State}
            | {ok, State, Timeout}
            | {ok, State, hibernate}
            | {stop, Reason :: term()}
            | ignore,
    State :: term(),
    Timeout :: non_neg_integer() | infinity.
%% ====================================================================
init(Values) ->
    do_init(Values).


%% handle_call/3
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:handle_call-3">gen_server:handle_call/3</a>
-spec handle_call(Request :: term(), From :: {pid(), Tag :: term()}, State :: term()) -> Result when
    Result :: {reply, Reply, NewState}
            | {reply, Reply, NewState, Timeout}
            | {reply, Reply, NewState, hibernate}
            | {noreply, NewState}
            | {noreply, NewState, Timeout}
            | {noreply, NewState, hibernate}
            | {stop, Reason, Reply, NewState}
            | {stop, Reason, NewState},
    Reply :: term(),
    NewState :: term(),
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ====================================================================
handle_call(Request, From, State) ->
    try
        do_call(Request, From, State)
    catch
        Type:Reason:Stack ->
            ?DEBUG("handle_call error:~n"
                         "Request:~p, From:~p, State:~p~n"
                         "Type:~p, Reason:~p",
                         [Request, From, State, Type, Reason]),
            ?DEBUG("stacktrace:~p",[Stack]),
            {reply, fail, State}
    end.


%% handle_cast/2
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:handle_cast-2">gen_server:handle_cast/2</a>
-spec handle_cast(Request :: term(), State :: term()) -> Result when
    Result :: {noreply, NewState}
            | {noreply, NewState, Timeout}
            | {noreply, NewState, hibernate}
            | {stop, Reason :: term(), NewState},
    NewState :: term(),
    Timeout :: non_neg_integer() | infinity.
%% ====================================================================
handle_cast(Msg, State) ->
    try
        do_cast(Msg, State)
    catch
        Type:Reason:Stack ->
            ?DEBUG("handle_cast error:~n"
                         "Msg:~p, State:~p~n"
                         "Type:~p, Reason:~p",
                         [Msg, State, Type, Reason]),
            ?DEBUG("stacktrace:~p",[Stack]),
            {noreply, State}
    end.


%% handle_info/2
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:handle_info-2">gen_server:handle_info/2</a>
-spec handle_info(Info :: timeout | term(), State :: term()) -> Result when
    Result :: {noreply, NewState}
            | {noreply, NewState, Timeout}
            | {noreply, NewState, hibernate}
            | {stop, Reason :: term(), NewState},
    NewState :: term(),
    Timeout :: non_neg_integer() | infinity.
%% ====================================================================
handle_info(Info, State) ->
    try
        do_info(Info, State)
    catch
        Type:Reason:Stack ->
            ?DEBUG("handle_info error:~n"
                         "Info:~p, State:~p~n"
                         "Type:~p, Reason:~p",
                         [Info, State, Type, Reason]),
            ?DEBUG("stacktrace:~p",[Stack]),
            {noreply, State}
    end.


%% terminate/2
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:terminate-2">gen_server:terminate/2</a>
-spec terminate(Reason, State :: term()) -> Any :: term() when
    Reason :: normal
            | shutdown
            | {shutdown, term()}
            | term().
%% ====================================================================
terminate(Reason, State) ->
    do_terminate(Reason, State).


%% code_change/3
%% ====================================================================
%% @doc <a href="http://www.erlang.org/doc/man/gen_server.html#Module:code_change-3">gen_server:code_change/3</a>
-spec code_change(OldVsn, State :: term(), Extra :: term()) -> Result when
    Result :: {ok, NewState :: term()} | {error, Reason :: term()},
    OldVsn :: Vsn | {down, Vsn},
    Vsn :: term().
%% ====================================================================
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.
